#ifndef CANVAS_H
#define CANVAS_H

#include <QCursor>
#include <QMap>
#include <QMenu>
#include <QWidget>

#include "../shape.h"

constexpr Qt::CursorShape CURSOR_DEFAULT = Qt::ArrowCursor;
constexpr Qt::CursorShape CURSOR_POINT = Qt::PointingHandCursor;
constexpr Qt::CursorShape CURSOR_DRAW = Qt::CrossCursor;
constexpr Qt::CursorShape CURSOR_MOVE = Qt::ClosedHandCursor;
constexpr Qt::CursorShape CURSOR_GRAB = Qt::OpenHandCursor;

constexpr double MOVE_SPEED = 5.0;

class Canvas : public QWidget
{
	Q_OBJECT

public:
	static const int CREATE;
	static const int EDIT;

	Canvas(double epsilon = 10.0, const QString& double_click = "close",
	       int num_backups = 10,
	       const QMap<QString, bool>& crosshair = {
		       {"polygon", false},
		       {"rectangle", true},
		       {"circle", false},
		       {"line", false},
		       {"point", false},
		       {"linestrip", false},
		       {"ai_polygon", false},
		       {"ai_mask", false},
	       });

	~Canvas();

	bool isShapeRestorable();

	bool isVisible(Shape* shape) const;

	bool drawing();

	bool editing();

	void setEditing(bool value = true);

	void unHighlight();

	bool selectedVertex() const;

	bool selectedEdge() const;

	void undoLastPoint();
	void setFillDrawing(bool value);
	void resetState();
	void restoreShape();
	void setCreateMode(QString value);
	void storeShapes();

	QList<Shape*> shapes;

	// Menus:
	// 0: right-click without selection and dragging of shapes
	// 1: right-click with selection and dragging of shapes
	QMenu* menus[2]{};

private:
	double epsilon;
	QString double_click;
	int num_backups;
	QMap<QString, bool> _crosshair;

	// polygon, rectangle, line, or point
	QString _createMode = "polygon";
	bool _fill_drawing = false;

	// Initialise local state.
	int mode = EDIT;
	QList<QList<Shape*>> shapesBackups;
	Shape* current = nullptr;
	QList<Shape*> selectedShapes; // save the selected shapes here
	QList<Shape*> selectedShapesCopy;

	// line represents:
	//   - createMode == 'polygon': edge from last point to current
	//   - createMode == 'rectangle': diagonal line of the rectangle
	//   - createMode == 'line': the line
	//   - createMode == 'point': the point
	Shape* line = nullptr;
	QPointF prevPoint{};
	QPointF prevMovePoint{};
	QPointF offsets[2]{};
	double scale = 1.0;
	QPixmap pixmap{};
	QHash<Shape*, bool> visible{};
	bool _hideBackround = false;
	bool hideBackround = false;
	Shape* hShape = nullptr;
	Shape* prevhShape = nullptr;
	int hVertex = -1;
	int prevhVertex = -1;
	int hEdge = -1;
	int prevhEdge = -1;
	bool movingShape = false;
	bool snapping = true;
	bool hShapeIsSelected = false;
	QPainter* _painter;
	QCursor _cursor = CURSOR_DEFAULT;

	int _ai_model = -1;

	bool fillDrawing();

	QString createMode();

	void initializeAiModel(const QString& name);

	void addPointToEdge();

	void removeSelectedPoint();

	bool endMove(bool copy);

	void hideBackroundShapes(bool value);

	void setHiding(bool enable = true);

	bool canCloseShape();

	void selectShapes(const QList<Shape*>& shapes);

	void selectShapePoint(const QPointF& point, bool multiple_selection_mode);

	void calculateOffsets(const QPointF& point);

	void boundedMoveVertex(const QPointF& pos);

	bool boundedMoveShapes(const QList<Shape*>& shapes, QPointF pos);

	void deSelectShape();

	QList<Shape*> deleteSelected();

	void deleteShape(Shape* shape);

	QPointF transformPos(const QPointF& point) const;

	QPointF offsetToCenter() const;

	bool outOfPixmap(const QPointF& point) const;

	void finalise();

	bool closeEnough(const QPointF& p1, const QPointF& p2) const;

	QPointF intersectionPoint(const QPointF& p1, const QPointF& p2) const;

	QVector<std::tuple<double, int, QPointF>>
	intersectingEdges(const QPointF& point1, const QPointF& point2, QList<QPointF> points) const;

	void moveByKeyboard(const QPointF& offset);

	Shape* setLastLabel(const QString& text, const std::map<QString, QString>& flags);

	void undoLastLine();


	void loadPixmap(const QPixmap& pixmap, bool clear_shapes);

	void loadShapes(const QList<Shape*>& shapes, bool replace);

	void restoreCursor();

	void overrideCursor(const QCursor& cursor);

	void setShapeVisible(Shape* shape, bool value);

protected:
	void enterEvent(QEnterEvent* event) override;

	void leaveEvent(QEvent* event) override;

	void focusOutEvent(QEvent* event);

	void mouseMoveEvent(QMouseEvent* event) override;

	void mousePressEvent(QMouseEvent* ev) override;

	void mouseReleaseEvent(QMouseEvent* ev) override;

	void mouseDoubleClickEvent(QMouseEvent* ev) override;

	void paintEvent(QPaintEvent* event) override;

	QSize sizeHint();

	QSize minimumSizeHint();

	void wheelEvent(QWheelEvent* ev) override;

	void keyPressEvent(QKeyEvent* ev) override;

	void keyReleaseEvent(QKeyEvent* ev) override;

signals:
	void zoomRequest(int delta, QPointF point);

	void scrollRequest(int dx, int dy);

	void newShape();

	void selectionChanged(const QList<Shape*>& selectedShapes);

	void shapeMoved();

	void drawingPolygon(bool drawing);

	void vertexSelected(bool selected);

	void mouseMoved(const QPointF& point);
};

#endif // CANVAS_H
